home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / THINKC / 3_0 / MENUDEMO / CRUSHMEN.C next >
C/C++ Source or Header  |  1989-02-08  |  13KB  |  478 lines

  1. /*
  2.  * crushmenu.c - a custom Menu Definition procedure.
  3.  */
  4.  
  5. #include <MemoryMgr.h>
  6. #include <Quickdraw.h>
  7. #include <MenuMgr.h>
  8. #include <ToolboxUtil.h>
  9.  
  10. #define ICONSIDE    32    /* Size (pixels) of one side of an icon */
  11. #define ICON_HMARG    4    /* Margin for left and right of an icon */
  12. #define ICON_VMARG    2    /* Margin for top and bottom of an icon */
  13.  
  14. #define HSLIDE    6    /* number of pixels each successive entry is shifted left */
  15. #define RMARG    5    /* pixels of blank to the right of the item text */
  16.  
  17. /*
  18.  * The menuData field of the menu's structure
  19.  * is a pile of variable-length fields.  Basically, it's:
  20.  *        Pascal string of the Title;
  21.  *        Pascal string of the first menu item;
  22.  *        struct iteminfo of the first menu item;
  23.  *        Pascal string of the second menu item...
  24.  * The list is ended by a Pascal string of length 0.
  25.  */
  26.  
  27. struct iteminfo {
  28.     char iconnum;    /* encoded Resource ID of the item's icon (or 0) */
  29.     char keyequiv;    /* its keyboard equivalent (or 0) */
  30.     char mark;        /* its marking character (or 0) */
  31.     Style tstyle;    /* its text's style */
  32. };
  33.  
  34. /*
  35.  * struct commons - common information each routine will be interested in.
  36.  *
  37.  * To save code, the main routine calculates the common information,
  38.  * then passes to each subroutine a pointer to the block of information.
  39.  */
  40.  
  41. struct commons {
  42.     FontInfo finfo;        /* the dimensions of the System Font */
  43.     int lheight;        /* the height of one text line */
  44.     int cloverwidth;    /* the width of the clover ("command") symbol */
  45.     int checkwidth;        /* the width of the check-mark character */
  46.     int numitems;        /* the number of items in the menu */
  47. };
  48.  
  49. /*
  50.  * main() - the entry-point to our custom menu definition.
  51.  *
  52.  * Because the MDEF must be a pure-code resource,
  53.  * we can't use any global or static data,
  54.  * which forces us to recalculate a lot of information
  55.  * each time we're called.
  56.  */
  57.  
  58. pascal void
  59. main(op, menuh, mrectp, pt, theitemp)
  60. int op;                /* the operation to perform */
  61. MenuHandle menuh;    /* the menu to operate on */
  62. Rect *mrectp;        /* the Rect enclosing the menu (global coordinates) */
  63. Point pt;            /* the mouse location (global coordinates) */
  64. int *theitemp;        /* the highlighted item */
  65. {
  66.     struct commons comm; /* the info of common interest */
  67.     char *mcp;        /* "Menu Char-Pointer" for traversing the menuData fields */
  68.     
  69.     /*
  70.      * Calculate the info we need regardless of the operation.
  71.      */
  72.     
  73.     GetFontInfo(&comm.finfo);
  74.     comm.lheight = comm.finfo.ascent + comm.finfo.descent + comm.finfo.leading;
  75.     comm.cloverwidth = CharWidth((char) 0x11);
  76.     comm.checkwidth = CharWidth((char) 0x12);
  77.     
  78.     comm.numitems = 0;
  79.     mcp = (char *) &((*menuh)->menuData);
  80.     mcp += ((int) *mcp & 0xFF) + 1;            /* skip past the title string */
  81.     while (*mcp) {
  82.         ++comm.numitems;
  83.         mcp += ((int) *mcp & 0xFF) + 1 + sizeof(struct iteminfo);
  84.     }
  85.     
  86.     /*
  87.      * Now do the requested operation.
  88.      */
  89.      
  90.     switch(op) {
  91.     case mSizeMsg:            /* find the dimensions of the menu */
  92.         msize(&comm, menuh);
  93.         break;
  94.     case mDrawMsg:            /* draw the menu */
  95.         mdraw(&comm, menuh, mrectp);
  96.         break;
  97.     case mChooseMsg:        /* find and highlight the item the mouse is in */
  98.         mchoose(&comm, menuh, mrectp, pt, theitemp);
  99.         break;
  100.     /* This MDEF doesn't support the popup message, mPopupMsg. */
  101.     }
  102. }
  103.  
  104. /*
  105.  * msize() - calculate the dimensions of the given menu,
  106.  * placing them in the appropriate fields of the menu structure.
  107.  */
  108.  
  109. msize(commp, menuh)
  110. struct commons *commp;    /* the common information */
  111. MenuHandle menuh;
  112. {
  113.     char *mcp;        /* "Menu Char-Pointer" for traversing the menuData fields */
  114.     int itemnum;    /* item number of the current item */
  115.     char *name;        /* the (Pascal) name of the current item */
  116.     struct iteminfo *infop;    /* pointer to the current item's info */
  117.     int thisheight;    /* height of this item */
  118.     int totalheight;/* total height of the menu */
  119.     int thiswidth;    /* width of the current item's text */
  120.     int maxright;    /* maximum width to the right of the reference point */
  121.     
  122.     maxright = 0;
  123.     totalheight = 0;
  124.     
  125.     itemnum = 0;
  126.     mcp = (char *) &((*menuh)->menuData);
  127.     mcp += ((int) *mcp & 0xFF) + 1;
  128.     while (*mcp) {
  129.         ++itemnum;
  130.         name = mcp;
  131.         mcp += ((int) *mcp & 0xFF) + 1;
  132.         infop = (struct iteminfo *) mcp;
  133.         mcp += sizeof(struct iteminfo);
  134.         
  135.         /*
  136.          * If we're drawing an icon, our line is that high,
  137.          * otherwise, the line is one text-line high.
  138.          * (This code assumes that the system font isn't huge.)
  139.          */
  140.         
  141.         if (infop->iconnum) {
  142.             thisheight = ICON_VMARG + ICONSIDE + ICON_VMARG;
  143.         } else {
  144.             thisheight = commp->lheight;
  145.         }
  146.         
  147.         /*
  148.          * Sum-up the item's width:
  149.          * The item contains:
  150.          *        a check (or other mark)            [optional]
  151.          *        an icon (and its margins)        [optional]
  152.          *        the item's name
  153.          *        a clover-key equivalent            [optional]
  154.          *        a right-margin
  155.          */
  156.     
  157.         thiswidth = 0;
  158.         
  159.         if (infop->mark) {
  160.             thiswidth += CharWidth(infop->mark);
  161.         } else {
  162.             thiswidth += commp->checkwidth;
  163.         }
  164.  
  165.         if (infop->iconnum) {
  166.             thiswidth += ICON_HMARG + ICONSIDE + ICON_HMARG;
  167.         }
  168.         
  169.         thiswidth += StringWidth(name);
  170.         
  171.         if (infop->keyequiv != '\0') {
  172.             thiswidth += commp->cloverwidth * 2 + commp->finfo.widMax;
  173.         }
  174.  
  175.         thiswidth += RMARG;
  176.         
  177.         /*
  178.          * Adjust the width by the amount this entry is shifted left.
  179.          */
  180.         
  181.         thiswidth -= (itemnum - 1) * HSLIDE;
  182.         if (maxright < thiswidth) {
  183.             maxright = thiswidth;
  184.         }
  185.         totalheight += thisheight;
  186.     }
  187.      
  188.     (*menuh)->menuHeight = commp->lheight / 2 + totalheight;
  189.     (*menuh)->menuWidth = commp->numitems * HSLIDE + maxright;
  190. }
  191.  
  192. /*
  193.  * mdraw() - draw the initial picture of the given menu within the given Rect.
  194.  */
  195.  
  196. mdraw(commp, menuh, mrectp)
  197. struct commons *commp;    /* the common information */
  198. MenuHandle menuh;
  199. Rect *mrectp;
  200. {
  201.     char *mcp;        /* "Menu Char-Pointer" for traversing the menuData fields */
  202.     int itemnum;    /* the item number of the current item */
  203.     char *name;        /* the (Pascal) name of the current item */
  204.     struct iteminfo *infop;    /* pointer to the current item's info */
  205.     Rect itemrect;    /* rect enclosing the current item */
  206.     Rect barrect;    /* rect enclosing the vertical bar for this item */
  207.     int textvert;    /* vertical position of text in this item */
  208.     PenState oldpen;/* the original Pen of the port (to be restored) */
  209.     Pattern fuzz;    /* pattern to dim with    */
  210.     int curhorz;    /* current horizontal position of the next piece */
  211.     Handle iconh;    /* handle to the current icon */
  212.     Rect iconrect;    /* Rect enclosing the icon to draw */
  213.     
  214.     GetPenState(&oldpen);
  215.  
  216.     /*
  217.      * Since we can't access the quickdraw global patterns
  218.      * (or even our own globals),
  219.      * we have to create this pattern each time we're called.
  220.      */
  221.  
  222.     fuzz[0] = (unsigned char) 0xAA;
  223.     fuzz[1] = (unsigned char) 0x55;
  224.     fuzz[2] = (unsigned char) 0xAA;
  225.     fuzz[3] = (unsigned char) 0x55;
  226.     fuzz[4] = (unsigned char) 0xAA;
  227.     fuzz[5] = (unsigned char) 0x55;
  228.     fuzz[6] = (unsigned char) 0xAA;
  229.     fuzz[7] = (unsigned char) 0x55;
  230.     
  231.     
  232.     /* EraseRect(mrectp);     The Menu Manager seems to do this for us */
  233.     
  234.     
  235.     itemrect.top = mrectp->top + commp->lheight / 2;
  236.     itemrect.left = mrectp->left + commp->numitems * HSLIDE;
  237.     itemrect.right = mrectp->right;
  238.     
  239.     itemnum = 0;
  240.     mcp = (char *) &((*menuh)->menuData);
  241.     mcp += ((int) *mcp & 0xFF) + 1;
  242.     while (*mcp) {
  243.         ++itemnum;
  244.         name = mcp;
  245.         mcp += ((int) *mcp & 0xFF) + 1;
  246.         infop = (struct iteminfo *) mcp;
  247.         mcp += sizeof(struct iteminfo);
  248.         
  249.         /*
  250.          * Calculate where in the menu this item goes:
  251.          * Its item rectangle, its associated vertical bar,
  252.          * and where (vertically) its text goes.
  253.          */
  254.  
  255.         if (infop->iconnum) {
  256.             itemrect.bottom = itemrect.top + ICON_VMARG + ICONSIDE + ICON_VMARG;
  257.         } else {
  258.             itemrect.bottom = itemrect.top + commp->lheight;
  259.         }
  260.         
  261.         barrect.left = itemrect.left - HSLIDE;
  262.         barrect.right = itemrect.left;
  263.         barrect.top = mrectp->top;
  264.         barrect.bottom = itemrect.bottom;
  265.         
  266.         textvert = (itemrect.top + itemrect.bottom) / 2 -
  267.               (commp->lheight / 2) + commp->finfo.ascent;
  268.         
  269.         /*
  270.          * Now draw the item:
  271.          *    a line demarking the vertical bar;
  272.          *    a line for empty items, otherwise...
  273.          *        the check (or other mark) [if any]
  274.          *        the icon [if any]
  275.          *        the text of the item
  276.          *        the clover-key equivalent [if any]
  277.          */
  278.         
  279.         MoveTo(barrect.right - 1, barrect.top);
  280.         LineTo(barrect.right - 1, itemrect.top - 1);
  281.         
  282.         TextFace(infop->tstyle);
  283.         
  284.         curhorz = itemrect.left;
  285.         if (name[1] == '-') {
  286.             MoveTo(curhorz, (itemrect.top + itemrect.bottom) / 2);
  287.             LineTo(itemrect.right - 1, (itemrect.top + itemrect.bottom) / 2);
  288.         } else {
  289.             if (infop->mark) {
  290.                 MoveTo(curhorz, textvert);
  291.                 DrawChar(infop->mark);
  292.                 curhorz += CharWidth(infop->mark);
  293.             } else {
  294.                 curhorz += commp->checkwidth;
  295.             }
  296.             
  297.             if (infop->iconnum) {
  298.             
  299.                 /*
  300.                  * Menu icon numbers start at 257.
  301.                  */
  302.                 
  303.                 iconh = GetIcon(((int) infop->iconnum & 0xFF) + 256);
  304.                 if (iconh) {
  305.                     iconrect.left = curhorz + ICON_HMARG;
  306.                     iconrect.top = itemrect.top + ICON_VMARG;
  307.                     iconrect.right = iconrect.left + ICONSIDE;
  308.                     iconrect.bottom = iconrect.top + ICONSIDE;
  309.                     PlotIcon(&iconrect, iconh);
  310.                 }
  311.                 curhorz += ICON_HMARG + ICONSIDE + ICON_HMARG;
  312.             }
  313.             
  314.             MoveTo(curhorz, textvert);
  315.             DrawString(name);
  316.             
  317.             if (infop->keyequiv != '\0') {
  318.                 MoveTo(itemrect.right -
  319.                   (commp->cloverwidth + commp->finfo.widMax + RMARG),
  320.                   textvert);
  321.                 DrawChar((char) 0x11);
  322.                 DrawChar(infop->keyequiv);
  323.             }
  324.         }
  325.         
  326.         /*
  327.          * The item is disabled if either the whole menu is disabled
  328.          * or the item can be and is disabled.
  329.          * (Only the first 31 items in a menu have disable/enable flags.)
  330.          *
  331.          * If the item is disabled, make it gray.
  332.          */
  333.         
  334.         if (((*menuh)->enableFlags & 1) == 0L || (itemnum < 32 &&
  335.           ((*menuh)->enableFlags & ((unsigned long) 1 << itemnum)) == 0L)) {
  336.             PenMode(patBic);
  337.             PenPat(&fuzz);
  338.             PaintRect(&itemrect);
  339.         }
  340.         
  341.         /*
  342.          * Set things up for the next item in the menu.
  343.          */
  344.         
  345.         SetPenState(&oldpen);
  346.         itemrect.top = itemrect.bottom;
  347.         itemrect.left -= HSLIDE;
  348.     }
  349. }
  350.  
  351. /*
  352.  * mchoose() - Find which item (if any) is selected by the given mouse location,
  353.  * unhighlight the previously-selected item (if any),
  354.  * and highlight the newly-selected item (again, if any).
  355.  */
  356.  
  357. mchoose(commp, menuh, mrectp, pt, theitemp)
  358. struct commons *commp;    /* the common information */
  359. MenuHandle menuh;
  360. Rect *mrectp;
  361. Point pt;
  362. int *theitemp;        /* if non-zero, the old item; set it to the new item */
  363. {
  364.     char *mcp;        /* "Menu Char-Pointer" for traversing the menuData fields */
  365.     int itemnum;    /* the item number of the current item */
  366.     char *name;        /* the (Pascal) name of the current item */
  367.     struct iteminfo *infop;    /* pointer to the current item's info */
  368.     Rect itemrect;    /* rect enclosing the current item */
  369.     Rect barrect;    /* rect enclosing the vertical bar for this item */
  370.     int wasitem;    /* item number of the previously-selected item */
  371.     int isitem;        /* ditto for the newly-selected item */
  372.     
  373.     /*
  374.      * See which item the mouse used to be in (rejecting nonsensical answers)
  375.      * and initialize our idea of where the mouse is
  376.      * (to be updated if/when we find the true mouse item).
  377.      */
  378.     
  379.     wasitem = *theitemp;
  380.     if (wasitem < 0 || wasitem > commp->numitems) {
  381.         
  382.         /*
  383.          * We get illegal wasitem numbers when the menu manager tries
  384.          * to blink the selected item.
  385.          * I haven't found any description of these illegal item numbers.
  386.          * If you know how to interpret these numbers, please drop me a note:
  387.          *        Brad Needham
  388.          *        2239 SE 74th Ave.
  389.          *        Hillsboro, OR 97123   USA
  390.          */
  391.          
  392.         wasitem = 0;
  393.     }
  394.     isitem = 0;
  395.     
  396.     /*
  397.      * Now traverse the menu, looking for the item containing the mouse
  398.      * or the item that used to contain the mouse.
  399.      *
  400.      * These two items are the only ones that will need to be redrawn --
  401.      * nothing else has changed appearance.
  402.      */
  403.     
  404.     itemrect.top = mrectp->top + commp->lheight / 2;
  405.     itemrect.left = mrectp->left + commp->numitems * HSLIDE;
  406.     itemrect.right = mrectp->right;
  407.     
  408.     itemnum = 0;
  409.     mcp = (char *) &((*menuh)->menuData);
  410.     mcp += ((int) *mcp & 0xFF) + 1;
  411.     while (*mcp) {
  412.         ++itemnum;
  413.         name = mcp;
  414.         mcp += ((int) *mcp & 0xFF) + 1;
  415.         infop = (struct iteminfo *) mcp;
  416.         mcp += sizeof(struct iteminfo);
  417.         
  418.         if (infop->iconnum) {
  419.             itemrect.bottom = itemrect.top + ICON_VMARG + ICONSIDE + ICON_VMARG;
  420.         } else {
  421.             itemrect.bottom = itemrect.top + commp->lheight;
  422.         }
  423.         
  424.         barrect.left = itemrect.left - HSLIDE;
  425.         barrect.right = itemrect.left;
  426.         barrect.top = mrectp->top;
  427.         barrect.bottom = itemrect.bottom;
  428.         
  429.         /*
  430.          * The item is disabled if either the whole menu is disabled
  431.          * or the item can be and is disabled.
  432.          * (Only the first 31 items in a menu have disable/enable flags.)
  433.          *
  434.          * If the item is disabled, we can't select it.
  435.          */
  436.         
  437.         if (((*menuh)->enableFlags & 1) == 0L || (itemnum < 32 &&
  438.           ((*menuh)->enableFlags & ((unsigned long) 1 << itemnum)) == 0L)) {
  439.             goto notenabled;
  440.         }
  441.         
  442.         /*
  443.          * If the mouse is inside this item, then
  444.          * this is the newly-selected item.
  445.          */
  446.         
  447.         if (PtInRect(pt, &itemrect) || PtInRect(pt, &barrect)) {
  448.             isitem = itemnum;
  449.         }
  450.         
  451.         /*
  452.          * We want to invert this item if either
  453.          * this item is the mouse's item (to be inverted) or
  454.          * this item used to be selected (and we want to clear the inversion).
  455.          *
  456.          * To avoid flicker, we change nothing if the mouse hasn't moved.
  457.          */
  458.         
  459.         if ((itemnum == isitem || itemnum == wasitem) &&
  460.           isitem != wasitem) {
  461.               InvertRect(&itemrect);
  462.               InvertRect(&barrect);
  463.         }
  464.  
  465. notenabled:    
  466.         itemrect.top = itemrect.bottom;
  467.         itemrect.left -= HSLIDE;
  468.     }
  469.     
  470.     /*
  471.      * Report either the found item,
  472.      * or the fact that we found no item.
  473.      */
  474.     
  475.     *theitemp = isitem;
  476.  
  477. }
  478.